import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * App 4 What
 *
 * @创建用户 cmic
 * @修改日期 2020/2/12
 * @其他说明
 */
public class App {
    /**
     * 特殊路径通配表达式
     */
    private static final Pattern SPECIAL_VIDEO_PATH = Pattern.compile("^\"(.*)\"$");

    /**
     * Jar包主目录
     *
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        // 先获取视频文件
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入视频文件路径:");
        String consoleInput;
        File mp4Source;
        while (true) {
            consoleInput = scanner.nextLine().trim();
            //
            Matcher matcher = SPECIAL_VIDEO_PATH.matcher(consoleInput);
            if (matcher.matches()) {
                consoleInput = matcher.group(1);
            }
            mp4Source = new File(consoleInput);
            if (!consoleInput.endsWith(".mp4")) {
                System.err.println("输入文件的格式错误,请重新输入");
            } else if (!mp4Source.exists()) {
                System.err.println("输入的视频文件路径错误,请重新输入");
            } else {
                break;
            }
        }
        System.out.println("将读取的视频数据源为:\t" + mp4Source.getAbsolutePath() + "\n");

        int cropImageWidth = 0;
        int cropImageHeight = 0;

        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(mp4Source);
            ff.setFrameRate(60);
            ff.setVideoBitrate(3000000);
            ff.start();
            // 获取帧长
            int length = ff.getLengthInFrames();
            int i = 0;
            Frame f;
            while (i < length) {
                f = ff.grabFrame();
                if (f.image != null) {
                    int srcImageWidth = cropImageWidth = f.imageWidth;
                    int srcImageHeight = cropImageHeight = f.imageHeight;
                    System.out.println("当前视频的截取图片像素为:Width:" + cropImageWidth + ";Height:" + cropImageHeight + "\n\n");
                    break;
                }
                i++;
            }
            ff.stop();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 先获取视频文件
        Scanner scannerRectangle = new Scanner(System.in);
        Rectangle rectangle2Crop = null;
        int xLocation = 0, yLocation = 0, widthX = 0, heightX = 0;
        for (int i = 0; i < 4; i++) {
            if (i == 1) {
                System.out.println("请输入截取区域在图片" + keywordTip4InputRectangle(cropImageWidth, cropImageHeight) + "中左上角像素纵坐标:");
                yLocation = Integer.parseInt(scanner.nextLine().trim());
            } else if (i == 2) {
                System.out.println("请输入截取区域的宽度:");
                widthX = Integer.parseInt(scanner.nextLine().trim());
            } else if (i == 3) {
                System.out.println("请输入截取区域的高度:");
                heightX = Integer.parseInt(scanner.nextLine().trim());
                rectangle2Crop = new Rectangle(xLocation, yLocation, widthX, heightX);
                System.out.println("将从视频的帧中" + keywordTip4InputRectangle(cropImageWidth, cropImageHeight) + "获取区域为(" + xLocation + "," + yLocation + "," + (xLocation + widthX) + "," + (yLocation + heightX) + "）的图像");
            } else {
                // 输入长度
                System.out.println("请输入截取区域在图片" + keywordTip4InputRectangle(cropImageWidth, cropImageHeight) + "中左上角像素横坐标:");
                xLocation = Integer.parseInt(scanner.nextLine().trim());
            }
        }

        System.out.println("3秒后开始截取视频帧中的部分图像...");
        Thread.sleep(3000);

        long videoDuration = getVideoDurationSecond(mp4Source);
        System.out.println("将要处理的视频播放时长为" + videoDuration + "秒");
        // 获取视频中的图片
        File pictureFrameOfVideo = new File("target/video-screen/temp.png");
        if (!pictureFrameOfVideo.getParentFile().exists()) {
            pictureFrameOfVideo.getParentFile().mkdirs();
        }
        // 开启一个进程，从视频中获取帧。并进行分析
        // 当前位置
        int currentFlag = 10;
        // 步长
        int stepLong = 60;
        // 真实坐标
        int realIndex = 0;
        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(mp4Source);
            ff.setFrameRate(60);
            ff.setVideoBitrate(3000000);
            ff.start();
            // 获取帧长
            int length = ff.getLengthInFrames();
            System.out.println("将要处理的视频总帧数为:" + length);
            int i = 0;
            Frame f;
            while (i < length) {
                f = ff.grabFrame();
                if (f.image != null && i == currentFlag) {

                    // 如果当前目标帧刚好为存在，则获取为分析帧，并将下一个分析帧设置为下一个步长
                    currentFlag += stepLong;
                    //
                    int srcImageWidth = f.imageWidth;
                    int srcImageHeight = f.imageHeight;
                    // 对截取的帧进行等比例缩放
                    // 创建Java2DFrameConverter用于在帧和BufferedImage之间复制数据
                    Java2DFrameConverter converter = new Java2DFrameConverter();
                    BufferedImage tempImage = converter.getBufferedImage(f);
                    BufferedImage image = new BufferedImage(srcImageWidth, srcImageHeight, BufferedImage.TYPE_4BYTE_ABGR);
                    image.getGraphics().drawImage(tempImage.getScaledInstance(srcImageWidth, srcImageHeight, Image.SCALE_SMOOTH), 0, 0,
                            null);
                    ++realIndex;
                    ImageIO.write(image, "png", pictureFrameOfVideo);
                    // 开始对图片进行分析
                    work2PullImg2Local(realIndex, rectangle2Crop);
                } else if (f.image == null) {
                    // 如果在目标帧刚好为Null,则获取下一帧作为分析帧
                    currentFlag++;
                }
                i++;
            }
            ff.stop();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 拉取手机的文件到达本地
     */
    private static void work2PullImg2Local(int index, Rectangle targetCropArea) {
        try {
            File handler = new File("target/video-screen/tmp-handler" + index + ".png");
            imageHandler(new File("target/video-screen/temp.png"), handler, targetCropArea);
            System.out.println("拉取文件成功并处理图片完毕，已经保存截取的图片到" + handler.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("拉取文件成功但处理图片失败，跳出循环!");
        }
    }


    /**
     * @param raw
     * @param handler
     * @throws IOException
     */
    private static void imageHandler(File raw, File handler, Rectangle targetCropArea) throws IOException {
        File f = raw;
        File t = handler;
        if (t.exists()) {
            t.delete();
        }
        //图片输入流
        ImageInputStream iis = ImageIO.createImageInputStream(f);

        //图片读取器
        Iterator<ImageReader> it = ImageIO.getImageReaders(iis);

        if (it.hasNext()) {
            ImageReader r = it.next();
            //设置输入流
            r.setInput(iis, true);
            //读取参数
            ImageReadParam param = r.getDefaultReadParam();
            //创建要截取的矩形范围
            //设置截取范围参数
            param.setSourceRegion(targetCropArea);
            //读取截图数据
            BufferedImage bi = r.read(0, param);
            // 非扩大的保存图片
            ImageIO.write(bi, "png", t);
        }
    }

    /**
     * 获取视频时长，单位为秒
     *
     * @param video 源视频文件
     * @return 时长（s）
     */
    public static long getVideoDurationSecond(File video) {
        long duration = 0L;
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
        try {
            ff.start();
            duration = ff.getLengthInTime() / (1000 * 1000);
            ff.stop();
        } catch (FrameGrabber.Exception e) {
            e.printStackTrace();
        }
        return duration;
    }

    /**
     * 输入
     *
     * @return
     */
    static String keywordTip4InputRectangle(int width, int height) {
        return "（" + width + " * " + height + ")";
    }
}
